package info.fqtech.skb.client.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import info.fqtech.skb.client.utils.HttpUtils;
import info.fqtech.skb.client.utils.SignUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author YASSER   作者 E-mail:   450153554@qq.com
 * @date: 2018-10-08 11:15
 * @version: 1.0.0
 * @description: <文件描述>
 * @modified: <文件修改说明>
 */
@Controller
@RequestMapping("/pay")
public class PayController {

    private final static Logger logger = LoggerFactory.getLogger(PayController.class);

    /**
     * 支付结果回调通知地址
     * 1、地址必须是公网可以访问
     * 2、地址不可有参数
     * 3、地址中不能出现 select|update|union|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute
     */
    private final String NOTIFY_URL = "http://XXXX.XX:8083/client/pay/notify";

    /**
     * 下单获取支付宝付款信息网关地址
     */
    private final String PAY_ORDER_GATEWAY = "http://skb.bfniu.com/zfb/pay/makeOrder";


    // 查询订单状态接口地址
    private final String QUERY_ORDER_STATUS_URL = "http://skb.bfniu.com/zfb/pay/queryStatus";

    /**
     * 收款吧的商户号。
     * 此处为测试商户号，生产环境需要找客服咨询对应的商户号
     */
    private final String MER_CODE = "891451";

    /**
     * 商户号下的收款账号
     * 此处为号，生产环境需要找客服咨询对应的收款账号
     */
    private final String MER_ACCOUNT = "8914518062";

    /**
     * 商户号下的收款账号对应的数据签名秘钥
     * 此处为测试秘钥，生产环境与MER_ACCOUNT对应
     */
    private final String MER_ACCOUNT_KEY = "5dcd4995f9e1bf6d4090f3525a6c9cef";

    /**
     * 模拟客户端下单接口
     * @param amount -- 支付金额，单位元，2位小数
     * @return
     */
    @ResponseBody
    @RequestMapping("/makeOrder")
    public Object makeOrder(Double amount) {
        // 创建订单号，需要商户内唯一，长度小于40位
        String orderNo = String.valueOf(System.currentTimeMillis());
        Map<String, String> requestMap = new HashMap<>(6);
        requestMap.put("merCode", MER_CODE);
        requestMap.put("account", MER_ACCOUNT);
        requestMap.put("amount", formatAmount(amount));
        requestMap.put("notifyUrl", NOTIFY_URL);
        requestMap.put("orderNo", orderNo);
        String sign = SignUtils.getSignOfMD5(requestMap, MER_ACCOUNT_KEY);
        requestMap.put("sign", sign);
        logger.info("requestMap:{}", requestMap);
        // 发起获取支付数据请求
        String responseStr = HttpUtils.doPost(PAY_ORDER_GATEWAY, requestMap);
        // 解析获取到的json字符串

        /**
         * 返回的数据为:
         * {
         *    "status": true|false, -- 下单成功true或失败false
         *    "msg": "status为false时有值，显示失败原因",
         *    "merCode": "商户号",
         *    "account": "收款账号",
         *    "orderNo": "订单号",
         *    "orderAmount": "订单金额，单位元，2位小数",
         *    "realPayAmount": "实际应支付金额，单位元，2位小数",
         *    "qrCodeUrl": "支付宝付款链接，可以在手机浏览器中直接打开，将会自动跳转到支付宝付款；也可以生成二维码使用支付宝扫码付款",
         *    "sign": "签名数据",
         * }
         * 备注：
         * 1、status为true时，merCode、account、orderNo、orderAmount、realPayAmount、qrCodeUrl、sign才有值
         * 2、sign签名算法：
         *    a、将merCode、account、orderNo、orderAmount、realPayAmount、qrCodeUrl参数放入集合集合中
         *    b、将集合中的参数根据参数名按照按字段名的ASCLL码从小到大排序后，并将非空的值按照URL的键值对的格式(即key1=value1&key2=value2)拼接成字符串strB
         *    c、将strB拼接上收款账号的秘钥key，形成strC = strB + "key=收款账号的秘钥key"
         *    d、sign = md5(strC)
         */
        JSONObject jsonObject = JSON.parseObject(responseStr);
        if(jsonObject.getBoolean("status")) {
            // 如果status为true。则表示下单成功
            // 处理下单成功的业务逻辑

            // ......


        }else {
            // 表示下单失败


        }
        return jsonObject;
    }

    /**
     * 处理后调通知
     * @param request
     * @param response
     */
    @RequestMapping("/notify")
    public void handleNotify(String status,
                               String msg,
                               String merCode,
                               String account,
                               String orderNo,
                               String orderAmount,
                               String realPayAmount,
                               String sign,
                               HttpServletRequest request, HttpServletResponse response) throws IOException {
        Map<String, String> notifyMap = new HashMap<>(5);
        notifyMap.put("merCode", merCode);
        notifyMap.put("account", account);
        notifyMap.put("orderNo", orderNo);
        notifyMap.put("orderAmount", orderAmount);
        notifyMap.put("realPayAmount", realPayAmount);
        logger.info("回调通知数据:{}", notifyMap);
        // 处理结果返回字符串
        String handleResultStr = "签名错误";
        // 计算签名
        String md5Sign = SignUtils.getSignOfMD5(notifyMap, MER_ACCOUNT_KEY);
        if(Objects.equals(md5Sign, sign)) {
            if (Objects.equals(status, String.valueOf(Boolean.TRUE))){
                // 如果status为true表示付款成功

                // 处理付款成功业务逻辑。此处需要防止重复通知

                // ......

                // 处理成功
                handleResultStr =  "SUCCESS";
            }
        }
        // 将处理结果返回给收款吧系统，如果返回SUCCESS，则表示处理成功，则收款吧系统不再发起通知。否则收款吧会定时3分钟再次发起，总共发起3次
        PrintWriter printWriter = response.getWriter();
        printWriter.write(handleResultStr);
        printWriter.flush();
    }


    /**
     * 查询订单状态
     * 此处建议订单创建后台30分钟后调用。
     * @param orderNo -- 订单号
     * @return
     */
    @RequestMapping("/queryOrder")
    public void queryOrder(String orderNo) {
        Map<String, String> requestMap = new HashMap<>(4);
        requestMap.put("merCode", MER_CODE);
        requestMap.put("account", MER_ACCOUNT);
        requestMap.put("orderNo", orderNo);
        String sign = SignUtils.getSignOfMD5(requestMap, MER_ACCOUNT_KEY);
        requestMap.put("sign", sign);
        String responseStr = HttpUtils.doGet(QUERY_ORDER_STATUS_URL, requestMap);
        JSONObject jsonObject = JSON.parseObject(responseStr);
        // 订单支付成功状态，true表示支付成功，false为不成功
        boolean status = jsonObject.getBoolean("status");
        if(status) {
            // 支付状态成功，进行数据签名验证
            Map<String, String> responseMap = new HashMap<>(8);
            responseMap.put("merCode", jsonObject.getString("merCode"));
            responseMap.put("account", jsonObject.getString("account"));
            responseMap.put("orderNo", jsonObject.getString("orderNo"));
            responseMap.put("orderAmount", jsonObject.getString("orderAmount"));
            responseMap.put("realPayAmount", jsonObject.getString("realPayAmount"));
            // 回调通知的签名
            String responseSign = jsonObject.getString("sign");
            // 计算签名
            String md5Sign = SignUtils.getSignOfMD5(responseMap, MER_ACCOUNT_KEY);
            if(Objects.equals(responseSign, md5Sign)){
                // 签名验证成功

                // ... 处理后续业务
            }
        }else {
            // 订单支付未成功，处理后续业务


        }
    }

    /**
     * 将订单金额格式化为2位小数的字符串
     * @param amount -- 订单金额，单位元，2位小数
     * @return
     */
    private String formatAmount(double amount) {
        BigDecimal bigDecimal = new BigDecimal(String.valueOf(amount));
        return bigDecimal.setScale(2, BigDecimal.ROUND_DOWN).toString();
    }

}
